Skip to content

Audio: Buffers: Add support for DP-to-DP component binding - #10562

Open
singalsu wants to merge 1 commit into
thesofproject:mainfrom
singalsu:audio_buffers_dp_to_dp_bind
Open

Audio: Buffers: Add support for DP-to-DP component binding#10562
singalsu wants to merge 1 commit into
thesofproject:mainfrom
singalsu:audio_buffers_dp_to_dp_bind

Conversation

@singalsu

Copy link
Copy Markdown
Collaborator

Previously binding two DP (Data Processing) scheduled components was rejected with IPC4_INVALID_REQUEST. This patch adds support for DP-to-DP binding by creating a dual ring buffer configuration where each DP module gets its own ring buffer on either side of the intermediate comp_buffer.

Data flow for DP-to-DP:
src_DP -> ring_buf_src -> comp_buffer -> ring_buf_sink -> sink_DP

Changes in helper.c:

  • Remove the DP-to-DP bind rejection in ipc_comp_connect().
  • Add src_is_dp, sink_is_dp, and dp_to_dp flags to detect the DP-to-DP case.
  • Create a second ring_buffer allocated from the source module's heap for the source side of the comp_buffer.
  • Track ring_buffer client_count on the DP heap with a NULL guard to avoid unsafe container_of when CONFIG_USERSPACE is disabled.

Changes in audio_buffer.c:

  • Change audio_buffer_attach_secondary_buffer() from a global rejection to per-side checks, allowing both secondary_buffer_sink and secondary_buffer_source to be set simultaneously.
  • Add a dual-secondary sync path in audio_buffer_sync_secondary_buffer()
    that cascades data through: input ring_buffer -> comp_buffer ->
    output ring_buffer, with rate-limiting applied on the output side.

Changes in ring_buffer.c:

  • Add DP heap client_count decrement in ring_buffer_free() with a NULL heap guard, matching the pattern used in comp_buffer_free().

@lgirdwood lgirdwood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@singalsu so this is not breaking anything atm, but its probably worth while to modify the nocodec topology to have this DP-DP config and see what happens...

@softwarecki
softwarecki self-requested a review February 20, 2026 15:56
@softwarecki

Copy link
Copy Markdown
Collaborator

No, no, no! No comp_buffer between dp modules! Just single ring buffer. We plan to remove comp_buffer and add support for dp-dp connection.

@singalsu

Copy link
Copy Markdown
Collaborator Author

No, no, no! No comp_buffer between dp modules! Just single ring buffer. We plan to remove comp_buffer and add support for dp-dp connection.

Would be great if that can be done! I need this to have decoder and phase_vocoder in same pipeline without LL components in between throttling the playback data. The phase vocoder consumes at input the audio data at 0.5x to 2.0x speed vs. normal. There can't be LL components with speed higher than 1.0.

@lgirdwood

Copy link
Copy Markdown
Member

No, no, no! No comp_buffer between dp modules! Just single ring buffer. We plan to remove comp_buffer and add support for dp-dp connection.

Would be great if that can be done! I need this to have decoder and phase_vocoder in same pipeline without LL components in between throttling the playback data. The phase vocoder consumes at input the audio data at 0.5x to 2.0x speed vs. normal. There can't be LL components with speed higher than 1.0.

@softwarecki whats your rough schedule for dp-dp ? It seems like we have a real use case now that needs it.

@singalsu
singalsu force-pushed the audio_buffers_dp_to_dp_bind branch from 46ecff9 to cdc456a Compare August 14, 2026 08:17
@singalsu singalsu changed the title [DNM] Audio: Buffers: Add support for DP-to-DP component binding Audio: Buffers: Add support for DP-to-DP component binding Aug 14, 2026
@singalsu
singalsu marked this pull request as ready for review August 14, 2026 08:18
Copilot AI lite review requested due to automatic review settings August 14, 2026 08:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the IPC4 binding and buffering infrastructure to allow binding two DP-scheduled components together by introducing a dual-ring-buffer “hybrid” topology around an intermediate comp_buffer, enabling lock-free DP access on both sides while preserving LL-cycle synchronization semantics.

Changes:

  • Allow DP→DP binds in ipc_comp_connect() and create/attach a second ring_buffer for the source side in the DP→DP case.
  • Update secondary-buffer attachment and syncing logic to permit and handle both secondary_buffer_sink and secondary_buffer_source simultaneously.
  • Add vregion refcount release in ring_buffer_free() to match new vregion refcounting during ring buffer creation.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/ipc/ipc4/helper.c Removes DP→DP bind rejection and adds dual ring-buffer creation/attachment for DP→DP connections.
src/audio/buffers/audio_buffer.c Allows per-side secondary attachments and adds a dual-secondary sync path for DP→DP cascaded copying.
src/audio/buffers/ring_buffer.c Releases vregion references during ring buffer free to match new vregion refcounting behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/audio/buffers/audio_buffer.c Outdated
singalsu added a commit to singalsu/sof that referenced this pull request Aug 14, 2026
See thesofproject#10562

Previously binding two DP (Data Processing) scheduled components
was rejected with IPC4_INVALID_REQUEST. This patch adds support
for DP-to-DP binding by creating a dual ring buffer configuration
where each DP module gets its own ring buffer on either side of
the intermediate comp_buffer.

Data flow for DP-to-DP:
  src_DP -> ring_buf_src -> comp_buffer -> ring_buf_sink -> sink_DP

Changes in helper.c:
- Remove the DP-to-DP bind rejection in ipc_comp_connect().
- Add src_is_dp, sink_is_dp, and dp_to_dp flags to detect the
  DP-to-DP case.
- Create a second ring_buffer allocated from the source module's
  mod_alloc_ctx for the source side of the comp_buffer.
- Refcount the DP vregion for each created ring_buffer via
  vregion_get(), with a NULL alloc guard.

Changes in audio_buffer.c:
- Change audio_buffer_attach_secondary_buffer() from a global
  rejection to per-side checks, allowing both secondary_buffer_sink
  and secondary_buffer_source to be set simultaneously.
- Add a dual-secondary sync path in audio_buffer_sync_secondary_buffer()
  that cascades data through: input ring_buffer -> comp_buffer ->
  output ring_buffer, with rate-limiting applied on the output side.

Changes in ring_buffer.c:
- Release the DP vregion in ring_buffer_free() via vregion_put()
  and free the mod_alloc_ctx when the refcount reaches zero,
  matching the pattern used in comp_buffer_free().

Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
@lgirdwood

Copy link
Copy Markdown
Member

@softwarecki @abonislawski this is needed very soon, do you have an ETA or can we go ahead here in the short term.

@singalsu
singalsu force-pushed the audio_buffers_dp_to_dp_bind branch from cdc456a to 70ff3f1 Compare August 24, 2026 10:17
@singalsu
singalsu requested a lite review from Copilot August 24, 2026 10:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread src/ipc/ipc4/helper.c Outdated
Comment thread src/audio/buffers/audio_buffer.c Outdated
@singalsu

Copy link
Copy Markdown
Collaborator Author

I just tested this updated patch with #11092, and it worked OK.

@singalsu
singalsu force-pushed the audio_buffers_dp_to_dp_bind branch from 70ff3f1 to 0570d14 Compare August 24, 2026 10:41
@singalsu
singalsu requested a review from ranj063 as a code owner August 24, 2026 10:41
@singalsu
singalsu requested a lite review from Copilot August 24, 2026 10:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread src/audio/module_adapter/module_adapter.c Outdated
@singalsu
singalsu force-pushed the audio_buffers_dp_to_dp_bind branch from 0570d14 to 58d6216 Compare August 24, 2026 10:59

@lgirdwood lgirdwood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@singalsu can we the logic behind a Kconfig so it will become easier to update when pipeline 2.0 is ready.

Comment thread src/audio/module_adapter/module_adapter.c Outdated
Comment thread src/audio/buffers/ring_buffer.c Outdated

@kv2019i kv2019i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks clean. A few questions inline. There are also tests failing, can you take a look at those.

Comment thread src/audio/buffers/audio_buffer.c Outdated
int audio_buffer_attach_secondary_buffer(struct sof_audio_buffer *buffer, bool at_input,
struct sof_audio_buffer *secondary_buffer)
{
#if CONFIG_DP_TO_DP_BIND

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style note, "#ifdef CONFIG_DP_TO_DP_BIND" is the usual convention. @lyakh agrees, but Linux kernel and statistics of use in SOF are on my side with this.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do I prefer #if? I don't know any more :-D I don't care that much really. I thought one was preferred and I tried to comply, but then we didn't find any written down preference for SOF. My current dilemma is on the one hand #if is shorter and is easier to extend with logical operations like #if CONFIG_A || CONFIG_B, but OTOH #ifdef is "cleaner" because when something isn't defined, it shouldn't really be possible to check its value...

Comment thread src/ipc/ipc4/helper.c
* ring_buffer_free for DP-to-DP binding)
*/
if (ring_buffer->audio_buffer.alloc)
vregion_get(ring_buffer->audio_buffer.alloc->vreg);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I don't fully get. Why do we need an additional vregion_get/put on the ringbuffer that we already allocated in the normal single DP case. This seems correct, but I'm puzzled why this ref is not taken in ring_buffer_create(). @lyakh any thoughts?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vregion reference counting was added because while we are allocating components and their data in and around the module-adapter, which is also where the vregion is created in the first place, we also create "normal" component buffers on that vregion. And while creation is done during component instantiation, which happens first, during freeing one of the buffers happens to be freed last - after the component. So, with ring buffers it wasn't needed until now because they are never created first or freed last. On the one hand refcounting them doesn't hurt (if done correctly) and might seem logical, OTOH if it isn't really needed - why add it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thanks @lyakh . Makes sense, but I still think without above addennum, the code is hard to understand. I we need further updates, I'd add some comment about this.

Comment thread src/ipc/ipc4/helper.c Outdated
MAX(obs, src_module_data->mpd.out_buff_size),
is_shared, buf_id);
if (!ring_buffer2) {
buffer_free(buffer);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the first ring_buffer we allocated (and the vregion ref), should we free those here?

Comment thread src/audio/buffers/audio_buffer.c Outdated
to_copy = MIN(MIN(data_available, free_size), limit);

err = source_to_sink_copy(data_src, data_dst, true, to_copy);
return err;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return source_to_sink_copy(data_src, data_dst, true, to_copy);

Comment thread src/audio/buffers/audio_buffer.c Outdated
if (buffer->secondary_buffer_sink && buffer->secondary_buffer_source) {
/*
* DP-to-DP case: both secondary buffers present.
* Data flows: input_ring_buffer -> comp_buffer -> output_ring_buffer

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering... I think the ring buffer was designed in a way to support asynchronous / lockless reading and writing (or something similar) and it should have been tailored to the use with DP. Shouldn't it be possible to just do DP -> ring_buffer -> DP?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, only one ring buffer should be used here.

if (alloc && alloc->vreg) {
if (!vregion_put(alloc->vreg))
rfree(alloc);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (alloc && alloc->vreg && !vregion_put(alloc->vreg))

Comment thread src/audio/buffers/audio_buffer.c Outdated
int audio_buffer_attach_secondary_buffer(struct sof_audio_buffer *buffer, bool at_input,
struct sof_audio_buffer *secondary_buffer)
{
#if CONFIG_DP_TO_DP_BIND

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do I prefer #if? I don't know any more :-D I don't care that much really. I thought one was preferred and I tried to comply, but then we didn't find any written down preference for SOF. My current dilemma is on the one hand #if is shorter and is easier to extend with logical operations like #if CONFIG_A || CONFIG_B, but OTOH #ifdef is "cleaner" because when something isn't defined, it shouldn't really be possible to check its value...

Comment thread src/ipc/ipc4/helper.c
* ring_buffer_free for DP-to-DP binding)
*/
if (ring_buffer->audio_buffer.alloc)
vregion_get(ring_buffer->audio_buffer.alloc->vreg);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vregion reference counting was added because while we are allocating components and their data in and around the module-adapter, which is also where the vregion is created in the first place, we also create "normal" component buffers on that vregion. And while creation is done during component instantiation, which happens first, during freeing one of the buffers happens to be freed last - after the component. So, with ring buffers it wasn't needed until now because they are never created first or freed last. On the one hand refcounting them doesn't hurt (if done correctly) and might seem logical, OTOH if it isn't really needed - why add it.

@intel-sofci

intel-sofci commented Aug 31, 2026

Copy link
Copy Markdown

PR 10562: test results

Run date: 2026-09-09 08:01 UTC

Tested commit: 053828bab86fbc6b4985c7f2f921af72c5eff1d5

mtl pass rate lnl pass rate ptl pass rate wcl pass rate nvl pass rate

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes core IPC binding and cross-domain buffer synchronization/lifetime semantics, which can impact correctness across cores and scheduling domains.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread src/audio/buffers/ring_buffer.c Outdated
Comment on lines +104 to +113
#if CONFIG_DP_TO_DP_BIND
/*
* For DP-to-DP binding: matches vregion_get() in ipc_comp_connect()
* for each ring_buffer. Releases the DP module's virtual memory region
* and frees the module allocation context when the refcount reaches zero.
*/
if (alloc && alloc->vreg) {
if (!vregion_put(alloc->vreg))
rfree(alloc);
}
Comment thread src/ipc/ipc4/helper.c Outdated
Comment on lines +954 to +960
#if CONFIG_DP_TO_DP_BIND
/* refcount the DP vregion for this ring_buffer (matches vregion_put in
* ring_buffer_free for DP-to-DP binding)
*/
if (ring_buffer->audio_buffer.alloc)
vregion_get(ring_buffer->audio_buffer.alloc->vreg);
#endif
@kv2019i
kv2019i self-requested a review September 4, 2026 08:46

@kv2019i kv2019i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@singalsu Can you rebase? I have no blocking issues left...

@lyakh lyakh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's clarify whether the ring -> comp -> ring buffer concept is acceptable or whether we want a better one from the beginning

@singalsu
singalsu force-pushed the audio_buffers_dp_to_dp_bind branch from 7c41c51 to 6ceffcb Compare September 7, 2026 08:08
@singalsu

singalsu commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

@singalsu Can you rebase? I have no blocking issues left...

It's now rebased. No other changes.

@singalsu
singalsu force-pushed the audio_buffers_dp_to_dp_bind branch from 6ceffcb to 229c19d Compare September 8, 2026 10:37
@singalsu
singalsu requested a lite review from Copilot September 8, 2026 10:37
@singalsu

singalsu commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

Note: New version with single ring buffer. It worked in my DP-DP topologies tests (phase vocoder, MFCC Whisper ASR offload), so I'm proposing this new version now.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There is a confirmed allocator-free mismatch in ring_buffer_free() and the dual-secondary synchronization/DP-to-DP design is inconsistent with the PR’s stated dataflow, risking incorrect runtime behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment thread src/audio/buffers/ring_buffer.c
Comment thread src/audio/buffers/audio_buffer.c
Comment thread src/ipc/ipc4/helper.c
Binding two DP (Data Processing) scheduled components was previously
rejected with IPC4_INVALID_REQUEST because both sides required a
secondary ring buffer. This patch adds support for DP-to-DP component
binding under a new CONFIG_DP_TO_DP_BIND Kconfig option.

In a DP-to-DP connection, a single shared ring buffer is created and
attached as a secondary buffer on both the source and sink sides of the
intermediate comp_buffer. The upstream DP module writes directly to the
ring buffer sink API, and the downstream DP module reads directly from
its source API. No copying or intermediate synchronization is required
during low-latency (LL) scheduling cycles.

The DP module virtual memory region backing the ring buffer is
refcounted so that it remains valid across component lifetimes, and
audio buffer reset and free operations ensure the shared secondary
buffer is not reset or freed twice.

Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The current secondary-buffer sync logic does not correctly support the dual-secondary-buffer scenario enabled by the new attachment rules, and the DP→DP implementation diverges from the PR’s described dual-ring-buffer design.

Review details

Suppressed comments (2)

src/audio/buffers/audio_buffer.c:76

  • audio_buffer_sync_secondary_buffer() prioritizes secondary_buffer_sink whenever it is set, so if both secondary_buffer_sink and secondary_buffer_source are set (the new attach behavior under CONFIG_DP_TO_DP_BIND allows this), the output-side sync is skipped. This makes the dual-secondary-buffer configuration effectively unsupported and can stall data on the output side.
	if (buffer->secondary_buffer_sink) {
		/*
		 * audio_buffer sink API is shadowed, that means there's a secondary_buffer
		 * at data input
		 * get data from secondary_buffer (use source API)

src/ipc/ipc4/helper.c:985

  • The DP-to-DP path implemented here attaches the same ring_buffer on both sides of the comp_buffer (and audio_buffer_sync_secondary_buffer() returns early when both secondary pointers are equal), which effectively bypasses the intermediate comp_buffer. This does not match the PR description that calls for two ring buffers (one per DP module heap) on either side of the comp_buffer; please align the implementation and/or the PR description (including the documented data-flow).
#ifdef CONFIG_DP_TO_DP_BIND
		if (dp_to_dp) {
			/*
			 * DP-to-DP binding: both source and sink are DP modules.
			 * A single shared ring_buffer is attached on both sides
			 * of the comp_buffer, so source DP writes directly to it
			 * and sink DP reads directly from it without copying.
			 */
			audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, true,
							     &ring_buffer->audio_buffer);
			audio_buffer_attach_secondary_buffer(&buffer->audio_buffer, false,
							     &ring_buffer->audio_buffer);
		} else
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@lgirdwood lgirdwood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @singalsu this will unblock us until pipeline 2.0 is ready.

struct sof_audio_buffer *secondary_buffer)
{
#ifdef CONFIG_DP_TO_DP_BIND
/* check per-side: allow attaching on both sides (needed for DP-to-DP) */

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the case of attaching the same buffer for the second time after one side has already been attached? Maybe rephrase the comment a bit to clarify that

audio_buffer_free(buffer->secondary_buffer_sink);
#ifdef CONFIG_DP_TO_DP_BIND
if (buffer->secondary_buffer_source != buffer->secondary_buffer_sink)
audio_buffer_free(buffer->secondary_buffer_source);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but will it be freed eventually on the second call?

if (buffer->secondary_buffer_source &&
buffer->secondary_buffer_source != buffer->secondary_buffer_sink &&
buffer->secondary_buffer_source->ops->reset)
buffer->secondary_buffer_source->ops->reset(buffer->secondary_buffer_source);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also here - will it ever be reset or is it intended that it never gets reset?

Comment thread src/ipc/ipc4/helper.c
else if (src_is_dp)
dp = source;
else
dp = NULL;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is now interesting. Previously buffers attached to DP modules were allocated on that DP module's vregion to have them accessible from that memory domain. How would this be resolved now? Do both DP modules on the two sides of the ring buffer have to belong to the same memory domain?..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants